#include "preHeader.h"
#include "OperatingActivityMgr.h"
#include "OperatingActivityFactory.h"
#include "Session/C2OperatingPacketHandler.h"
#include "Session/S2OperatingPacketHandler.h"
#include "Session/GameServerMgr.h"
#include "Session/DBPSession.h"
#include "Game/TransMgr.h"

OperatingActivityMgr::OperatingActivityMgr()
: IServerService("OperatingActivityMgr")
, sWheelTimerMgr(1, GET_UNIX_TIME)
, m_actUITypePkt(SMSG_OPERATING_GET_TYPE_RESP)
, m_lastEventTime(-1)
{
}

OperatingActivityMgr::~OperatingActivityMgr()
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		delete pActInst;
	}
}

int OperatingActivityMgr::HandleClientPacket(uint32 uid, INetPacket& pck)
{
	return sC2OperatingPacketHandler.HandlePacket(this, pck, uid);
}

int OperatingActivityMgr::HandleServerPacket(INetPacket& pck)
{
	return sS2OperatingPacketHandler.HandlePacket(this, pck);
}

WheelTimerMgr *OperatingActivityMgr::GetWheelTimerMgr()
{
	return &sWheelTimerMgr;
}

bool OperatingActivityMgr::Initialize()
{
	while (sGameServerMgr.GetServerId() == 0) {
		NLOG("Waiting game server register ...");
		OS::SleepS(1);
	}
	CreateTimerX(std::bind(&OperatingActivityMgr::CleanupSaveData, this),
		0, 1, GET_DAY_UNIX_TIME + ONE_DAY * 3);
	ReloadData();
	return FrameWorker::Initialize();
}

void OperatingActivityMgr::Update(uint64 diffTime)
{
	IServerService::UpdateAllPackets();
	AsyncTaskOwner::UpdateTask();
}

void OperatingActivityMgr::OnTick()
{
	sWheelTimerMgr.Update(GET_UNIX_TIME);
}

template <typename T>
bool IsActivityServerEnable(const T& actCfg, uint32 serverId) {
	if (!actCfg.strOpenServers.empty()) {
		return IsKeyValueInRangeString(actCfg.strOpenServers, serverId);
	} else {
		return !IsKeyValueInRangeString(actCfg.strNotOpenServers, serverId);
	}
}

void OperatingActivityMgr::CleanupSaveData()
{
	NetPacket rpcReqPck(CDBP_CLEANUP_OPERATING_ACTIVITY);
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		rpcReqPck << actUniqueKey;
	}
	sDBPSession.RPCInvoke(rpcReqPck);
}

void OperatingActivityMgr::ReloadData()
{
	auto serverId = sGameServerMgr.GetServerId();
	auto startServerDayTime = System::GetDayUnixTime(
		sGameServerMgr.GetStartServerTime());
	auto pActTable = sDBMgr.GetTable<OperatingActivity>();
	for (auto& actProto :
		MakeIteratorRange(pActTable->VBegin(), pActTable->VEnd())) {
		const_cast<int64&>(actProto.nActvtStopTime) = FixActvtTime(
			actProto.nActvtReferTime, actProto.nActvtStopTime, startServerDayTime);
		const_cast<int64&>(actProto.nShowStartTime) = FixActvtTime(
			actProto.nActvtReferTime, actProto.nShowStartTime, startServerDayTime);
		const_cast<int64&>(actProto.nShowEndTime) = FixActvtTime(
			actProto.nActvtReferTime, actProto.nShowEndTime, startServerDayTime);
		const_cast<int64&>(actProto.nPlayStartTime) = FixActvtTime(
			actProto.nActvtReferTime, actProto.nPlayStartTime, startServerDayTime);
		const_cast<int64&>(actProto.nPlayEndTime) = FixActvtTime(
			actProto.nActvtReferTime, actProto.nPlayEndTime, startServerDayTime);
	}

	std::unordered_set<uint64> activeActivityList;
	activeActivityList.reserve(pActTable->GetNumRows());
	for (auto& actProto :
		MakeIteratorRange(pActTable->VBegin(), pActTable->VEnd())) {
		if (IsActivityExpired(actProto)) {
			continue;
		}
		if (!IsActivityServerEnable(actProto, serverId)) {
			continue;
		}
		auto actUniqueKey = GetOperatingUniqueKey(actProto);
		auto itr = m_mapActInst.find(actUniqueKey);
		if (itr == m_mapActInst.end()) {
			m_mapActInst.emplace(actUniqueKey,
				OperatingActivityFactory::Create(&actProto)).
				first->second->InitActivityInstance();
		} else {
			itr->second->ResetActivityPrototype(&actProto);
		}
		activeActivityList.insert(actUniqueKey);
	}

	for (auto itr = m_mapActInst.begin(); itr != m_mapActInst.end();) {
		if (activeActivityList.count(itr->first) == 0) {
			delete itr->second;
			itr = m_mapActInst.erase(itr);
		} else {
			++itr;
		}
	}

	RestartActivityTimer(true);
}

void OperatingActivityMgr::UpdateAllActivityStatus()
{
	auto pDateTime = GET_ATOMIC_DATE_TIME;
	auto uDayTime = System::GetDayUnixTime(*pDateTime);
	auto isNewDay = m_lastEventTime < uDayTime;
	m_lastEventTime = pDateTime->time_;

	for (auto itr = m_mapActInst.begin(); itr != m_mapActInst.end();) {
		auto pActInst = itr->second;
		if (isNewDay) {
			pActInst->OnDayEnd();
		}
		auto pProto = pActInst->GetProto();
		if (pProto->nPlayEndTime < m_lastEventTime) {
			if (!pActInst->IsActivityEnd()) {
				pActInst->OnActivityEnd();
			}
		}
		if (pProto->nActvtStopTime < m_lastEventTime) {
			delete pActInst;
			itr = m_mapActInst.erase(itr);
		} else {
			++itr;
		}
	}

	NetPacket pck(SGX_SAVE_LAST_OPERATING_EVENT_TIME);
	pck << (int64)m_lastEventTime;
	sGameServerMgr.SendPacket2GS(pck);

	RestartActivityTimer();
}

void OperatingActivityMgr::RestartActivityTimer(bool isReset)
{
	if (isReset) {
		RemoveTimers(TimerTypeOperatingActivity);
	}
	BroadcastActivityListDirty();
	CreateTimer(std::bind(&OperatingActivityMgr::UpdateAllActivityStatus, this),
		TimerTypeOperatingActivity, 0, 1, GetActivityNextEventTime());
}

time_t OperatingActivityMgr::GetActivityNextEventTime() const
{
	auto nextEventTime = System::GetDayUnixTime(m_lastEventTime) + ONE_DAY;
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		auto pProto = pActInst->GetProto();
		if (m_lastEventTime <= pProto->nPlayEndTime) {
			nextEventTime = std::min<time_t>(nextEventTime, pProto->nPlayEndTime);
		} else if (m_lastEventTime <= pProto->nActvtStopTime) {
			nextEventTime = std::min<time_t>(nextEventTime, pProto->nActvtStopTime);
		}
	}
	return nextEventTime;
}

void OperatingActivityMgr::InitLastEventTime(time_t lastEventTime)
{
	if (m_lastEventTime == -1) {
		if (lastEventTime != -1) {
			m_lastEventTime = std::min(lastEventTime, GET_UNIX_TIME);
		} else {
			m_lastEventTime = GET_UNIX_TIME;
		}
	}
}

void OperatingActivityMgr::ReloadActivityUIType()
{
	m_actUITypePkt.Clear();
	auto serverId = sGameServerMgr.GetServerId();
	auto pActUITable = sDBMgr.GetTable<OperatingActivityUIType>();
	for (auto& actUIType :
		MakeIteratorRange(pActUITable->VBegin(), pActUITable->VEnd())) {
		if (!IsActivityServerEnable(actUIType, serverId)) {
			continue;
		}
		m_actUITypePkt << actUIType.nUIType << actUIType.strClientParams;
	}
	sTransMgr.BroadcastPacket2AllClientSafe(m_actUITypePkt);
}

void OperatingActivityMgr::BuildActivityShortShotPacket(const OperatingPlayerInfo& info, uint64 actUniqueKey)
{
	GErrorCode errCode = CommonSuccess;
	NetPacket pck(SMSG_OPERATING_GET_SHORT_HOT_RESP, actUniqueKey);
	const size_t anchor = pck.Placeholder<int32>(errCode);
	do {
		auto itr = m_mapActInst.find(actUniqueKey);
		if (itr == m_mapActInst.end()) {
			errCode = ErrOperatingNotExist;
			break;
		}
		auto pActInst = itr->second;
		if ((errCode = GetActivityShowStatus(info, pActInst)) != CommonSuccess) {
			break;
		}
		pck << pActInst->HasShortHot(info);
	} while (0);
	if (errCode != CommonSuccess) {
		pck.Put(anchor, (int32)errCode);
	}
	sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
}

void OperatingActivityMgr::BuildActivityDetailPacket(const OperatingPlayerInfo& info, uint64 actUniqueKey)
{
	GErrorCode errCode = CommonSuccess;
	NetPacket pck(SMSG_OPERATING_GET_DETAIL_RESP, actUniqueKey);
	const size_t anchor = pck.Placeholder<int32>(errCode);
	do {
		auto itr = m_mapActInst.find(actUniqueKey);
		if (itr == m_mapActInst.end()) {
			errCode = ErrOperatingNotExist;
			break;
		}
		auto pActInst = itr->second;
		if ((errCode = GetActivityShowStatus(info, pActInst)) != CommonSuccess) {
			break;
		}
		pck << pActInst->HasShortHot(info);
		pActInst->BuildActivityDetailPacket(pck, info);
	} while (0);
	if (errCode != CommonSuccess) {
		pck.Put(anchor, (int32)errCode);
	}
	sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
}

void OperatingActivityMgr::BuildActivityListPacket(const OperatingPlayerInfo& info, int32 actUIType)
{
	uint32 actCnt = 0;
	NetPacket pck(SMSG_OPERATING_GET_LIST_RESP, actUIType);
	const size_t anchor = pck.Placeholder(actCnt);
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		auto pProto = pActInst->GetProto();
		if (actUIType != -1 && actUIType != pProto->nUIType) {
			continue;
		}
		auto errCode = GetActivityListStatus(info, pActInst);
		if (errCode != CommonSuccess) {
			continue;
		}
		pck << GetOperatingUniqueKey(*pProto);
		pck << pActInst->HasShortHot(info);
		pActInst->BuildActivityIntroPacket(pck, info);
		++actCnt;
	}
	pck.Put(anchor, actCnt);
	sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
}

void OperatingActivityMgr::BuildTotalActivityTypePacket(uint32 playerId)
{
	sTransMgr.SendPacket2ClientSafe(playerId, m_actUITypePkt);
}

void OperatingActivityMgr::OnPlayerBuy(const OperatingPlayerInfo& info, uint64 actUniqueKey, uint32 index)
{
	GErrorCode errCode = CommonSuccess;
	NetPacket pck(SMSG_OPERATING_BUY_RESP, actUniqueKey);
	const size_t anchor = pck.Placeholder<int32>(errCode);
	do {
		auto itr = m_mapActInst.find(actUniqueKey);
		if (itr == m_mapActInst.end()) {
			errCode = ErrOperatingNotExist;
			break;
		}
		auto pActInst = itr->second;
		if ((errCode = GetActivityStatus(info, pActInst)) != CommonSuccess) {
			break;
		}
		errCode = pActInst->OnPlayerBuy(pck, info, index);
	} while (0);
	if (errCode != CommonSuccess) {
		pck.Put(anchor, (int32)errCode);
	}
	sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
	if (errCode == CommonSuccess) {
		BuildActivityDetailPacket(info, actUniqueKey);
	}
}

void OperatingActivityMgr::OnPlayerGetReward(const OperatingPlayerInfo& info, uint64 actUniqueKey, uint32 index)
{
	GErrorCode errCode = CommonSuccess;
	NetPacket pck(SMSG_OPERATING_GET_REWARD_RESP, actUniqueKey);
	const size_t anchor = pck.Placeholder<int32>(errCode);
	do {
		auto itr = m_mapActInst.find(actUniqueKey);
		if (itr == m_mapActInst.end()) {
			errCode = ErrOperatingNotExist;
			break;
		}
		auto pActInst = itr->second;
		if ((errCode = GetActivityStatus(info, pActInst)) != CommonSuccess) {
			break;
		}
		errCode = pActInst->OnPlayerGetReward(pck, info, index);
	} while (0);
	if (errCode != CommonSuccess) {
		pck.Put(anchor, (int32)errCode);
	}
	sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
	if (errCode == CommonSuccess) {
		BuildActivityDetailPacket(info, actUniqueKey);
	}
}

void OperatingActivityMgr::OnPlayerLogin(const OperatingPlayerInfo& info)
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerLogin(info)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

void OperatingActivityMgr::OnPlayerPay(const OperatingPlayerInfo& info, uint32 rmbVal, uint32 goldVal, uint32 payType)
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerPay(info, rmbVal, goldVal, payType)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

void OperatingActivityMgr::OnPlayerCost(const OperatingPlayerInfo& info, uint32 goldVal, uint32 costType)
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerCost(info, goldVal, costType)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

void OperatingActivityMgr::OnPlayerGainScore(const OperatingPlayerInfo& info, uint32 scoreValue, uint32 scoreType)
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerGainScore(info, scoreValue, scoreType)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

void OperatingActivityMgr::OnPlayerPlayActvt(const OperatingPlayerInfo& info, OperatingPlayType playType, const uint32 params[])
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerPlayActvt(info, playType, params)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

void OperatingActivityMgr::OnPlayerEventActvt(const OperatingPlayerInfo& info, OperatingEventType eventType, const uint32 params[])
{
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		if (GetActivityStatus(info, pActInst) != CommonSuccess) {
			continue;
		}
		if (!pActInst->OnPlayerEventActvt(info, eventType, params)) {
			continue;
		}
		TryShowActivityShortHotStatus(info, pActInst);
	}
}

bool OperatingActivityMgr::IsPreActivityFinished(const OperatingPlayerInfo& info, const OperatingActivityBase* pActInst) const
{
	auto pProto = pActInst->GetProto();
	if (pProto->nPreType == (u32)OperatingActivityType::None) {
		return true;
	}
	auto actPreUniqueKey = GetOperatingUniqueKey(
		pProto->nPreType, pProto->nPreTimes);
	auto itr = m_mapActInst.find(actPreUniqueKey);
	if (itr == m_mapActInst.end()) {
		return true;
	}
	auto pPreActInst = itr->second;
	if (pPreActInst->IsFinished(info)) {
		return true;
	}
	return false;
}

GErrorCode OperatingActivityMgr::GetActivityStatus(const OperatingPlayerInfo& info, const OperatingActivityBase* pActInst) const
{
	auto pProto = pActInst->GetProto();
	if (!IsActivityPlaying(*pProto)) {
		return ErrOperatingNotPlaying;
	}
	if (pProto->nPlayReqVipLevel > info.vipLevel) {
		return ErrOperatingPreVipLevel;
	}
	if (pProto->nPlayReqLevel > info.level) {
		return ErrOperatingPreLevel;
	}
	if (!IsPreActivityFinished(info, pActInst)) {
		return ErrOperatingPreType;
	}
	return CommonSuccess;
}

GErrorCode OperatingActivityMgr::GetActivityShowStatus(const OperatingPlayerInfo& info, const OperatingActivityBase* pActInst) const
{
	auto pProto = pActInst->GetProto();
	if (!IsActivityShowing(*pProto)) {
		return ErrOperatingNotShowing;
	}
	if (pProto->nShowReqVipLevel > info.vipLevel) {
		return ErrOperatingPreVipLevel;
	}
	if (pProto->nShowReqLevel > info.level) {
		return ErrOperatingPreLevel;
	}
	if (!IsPreActivityFinished(info, pActInst)) {
		return ErrOperatingPreType;
	}
	return CommonSuccess;
}

GErrorCode OperatingActivityMgr::GetActivityListStatus(const OperatingPlayerInfo& info, const OperatingActivityBase* pActInst) const
{
	auto errCode = GetActivityShowStatus(info, pActInst);
	if (errCode != CommonSuccess) {
		return errCode;
	}
	if (pActInst->IsFinished(info)) {
		return ErrOperatingFinished;
	}
	if (pActInst->IsHide(info)) {
		return ErrOperatingHide;
	}
	return CommonSuccess;
}

void OperatingActivityMgr::TryShowActivityShortHotStatus(const OperatingPlayerInfo& info, const OperatingActivityBase* pActInst) const
{
	if (pActInst->HasShortHot(info)) {
		auto actUniqueKey = GetOperatingUniqueKey(*pActInst->GetProto());
		NetPacket pck(SMSG_OPERATING_GET_SHORT_HOT_RESP);
		pck << actUniqueKey << (int32)CommonSuccess << true;
		sTransMgr.SendPacket2ClientSafe(info.playerId, pck);
	}
}

void OperatingActivityMgr::BroadcastAllActivityCares() const
{
	std::bitset<(int)OperatingCareType::Count> operatingCares;
	for (auto& [actUniqueKey, pActInst] : m_mapActInst) {
		pActInst->LoadActivityCares(operatingCares);
	}
	NetPacket pck(SGX_SYNC_OPERATING_CARES);
	pck << operatingCares.to_string();
	sGameServerMgr.SendPacket2GS(pck);
}

void OperatingActivityMgr::BroadcastActivityListDirty()
{
	NetPacket pck(SMSG_OPERATING_LIST_DIRTY);
	sTransMgr.BroadcastPacket2AllClientSafe(pck);
}

bool OperatingActivityMgr::IsActivityExpired(const OperatingActivity& actProto)
{
	return GET_UNIX_TIME > actProto.nActvtStopTime;
}

bool OperatingActivityMgr::IsActivityShowing(const OperatingActivity& actProto)
{
	return IsInRange<time_t>(GET_UNIX_TIME,
		actProto.nShowStartTime, actProto.nShowEndTime);
}

bool OperatingActivityMgr::IsActivityPlaying(const OperatingActivity& actProto)
{
	return IsInRange<time_t>(GET_UNIX_TIME,
		actProto.nPlayStartTime, actProto.nPlayEndTime);
}

OperatingPlayerInfo OperatingActivityMgr::UnpackPlayerInfo(INetStream& pck)
{
	OperatingPlayerInfo info;
	pck >> info.playerId >> info.vipLevel >> info.level;
	return info;
}
